<?php
/**
 * Created by PhpStorm.
 * User: DELL-PC
 * Date: 2018/8/11
 * Time: 16:12
 */

namespace J\Schema\Collection\Element;


use J\Schema\IElement;
use J\Schema\Syntax;
use J\Util\DateTool;

abstract class Helper implements IHelper
{
    /**
     * 助手工厂
     * @param IElement $element
     * @return IHelper
     */
    public static function factory($element)
    {
        $type =  $element->getColumnType();

        if ($type === Syntax::VALUE_ID) return new IdHelper();
        if ($type === Syntax::VALUE_IP) return new IpHelper();
        if ($type === Syntax::VALUE_BOOL) return new BoolHelper();

        if ($type & Syntax::VALUE_SELECT) return new SelectHelper($element);

        switch ($type & Syntax::MASK_MAIN_TYPE) {
            case Syntax::VALUE_INTEGER :
                return new IntHelper();
            case Syntax::VALUE_FLOAT :
                return new DoubleHelper();
            case Syntax::VALUE_DATE_TIME :
                return new DateHelper($element);
            default :
        }

        if ($type & Syntax::MASK_SUBTYPE) return new TextHelper();

        return new StringHelper();
    }

    public function bind()
    {
        return \PDO::PARAM_STR;
    }

    abstract public function adaptValue(&$value, $op = null);

    abstract public function getCalculators();

    public function isCalculator($operator)
    {
        return in_array($operator, $this->getCalculators());
    }

    abstract public function getCompares();

    public function isCompare($operator)
    {
        return in_array($operator, $this->getCompares());
    }

    public function isEmptyCalculator($op)
    {
        return in_array($op, [Syntax::OPERATOR_RAND, Syntax::OPERATOR_NOW, '!']);
    }

    public function isArrayCalculator($op = null)
    {
        return false;
    }

    public function isEmptyCompare($op)
    {
        return in_array($op, [Syntax::OPERATOR_NOT_NULL, Syntax::OPERATOR_IS_NULL]);
    }

    public function isArrayCompare($op)
    {
        return in_array($op, [Syntax::OPERATOR_IN, Syntax::OPERATOR_NOT_IN, Syntax::OPERATOR_BETWEEN]);
    }

    public function display(&$value)
    {
        return $this->adaptValue($value);
    }

    public function getDefaultValue()
    {
        return '';
    }

    public function getFieldType()
    {
        return 'input';
    }

}

class BoolHelper extends Helper
{
    public function adaptValue(&$value, $op = null)
    {
        convert_bool($value);
        return true;
    }

    public function bind($op = null)
    {
        return \PDO::PARAM_BOOL;
    }

    public function getCalculators()
    {
        return ['!'];
    }

    public function getCompares()
    {
        return ['=', '<>', Syntax::OPERATOR_NOT_NULL, Syntax::OPERATOR_IS_NULL];
    }

    public function getDefaultValue()
    {
        return false;
    }

    public function getFieldType()
    {
        return "switch";
    }
}

class IntHelper extends Helper
{
    public function adaptValue(&$value, $op = null)
    {
        return convert_int($value);
    }

    public function bind($op = null)
    {
        return \PDO::PARAM_INT;
    }

    public function getCalculators()
    {
        return ['++', '--', '+', '-', '*', '/', '%', '~', '|', '&', '<<', '>>', Syntax::OPERATOR_RAND];
    }

    public function getCompares()
    {
        return ['<', '<=', '>', '>=', '=', '<>',
            Syntax::OPERATOR_IN, Syntax::OPERATOR_NOT_IN,
            Syntax::OPERATOR_NOT_NULL, Syntax::OPERATOR_IS_NULL,
            Syntax::OPERATOR_BETWEEN
        ];
    }

    public function getDefaultValue()
    {
        return 0;
    }
}

class StringHelper extends Helper
{
    public function adaptValue(&$value, $op = null)
    {
        $value = (string)$value;
        return true;
    }

    public function getCalculators()
    {
        return ['.', Syntax::OPERATOR_REPLACE];
    }

    public function getCompares()
    {
        return ['=',
            Syntax::OPERATOR_LIKE,Syntax::OPERATOR_NOT_LIKE,
            Syntax::OPERATOR_IN, Syntax::OPERATOR_NOT_IN,
            Syntax::OPERATOR_NOT_NULL, Syntax::OPERATOR_IS_NULL,
        ];
    }

    public function isArrayCalculator($op = null)
    {
        return in_array($op, ['.', Syntax::OPERATOR_REPLACE]);
    }
}

class TextHelper extends StringHelper
{
    public function getFieldType()
    {
        return 'textarea';
    }
}

class DoubleHelper extends Helper
{
    public function adaptValue(&$value, $op = null)
    {
        return convert_float($value);
    }

    public function getCalculators()
    {
        return ['+', '-', '*', '/',
            Syntax::OPERATOR_RAND
        ];
    }

    public function getCompares()
    {
        return ['<', '<=', '>', '>=', '=', '<>',
            Syntax::OPERATOR_IN, Syntax::OPERATOR_NOT_IN,
            Syntax::OPERATOR_NOT_NULL, Syntax::OPERATOR_IS_NULL,
            Syntax::OPERATOR_BETWEEN
        ];
    }

    public function getDefaultValue()
    {
        return 0;
    }
}

class DateHelper extends Helper
{
    /**
     * @var string
     */
    protected $formatter;

    /**
     * @var string
     */
    protected $holder = null;

    /**
     * DateHelper constructor.
     * @param IElement $element
     */
    public function __construct($element)
    {
        $this->formatter = $this->getFormatter($element);
        $this->holder = $this->findFieldType($element);

    }

    public function bind($op = null)
    {
        return \PDO::PARAM_INT;
    }

    public function adaptValue(&$value, $op = null)
    {
        $value = DateTool::strToInt($value);
        return $value !== null;
    }

    public function getCalculators()
    {
        return [Syntax::OPERATOR_NOW];
    }

    public function getCompares()
    {
        return ['<', '<=', '>', '>=', '=', '<>',
            Syntax::OPERATOR_IN, Syntax::OPERATOR_NOT_IN,
            Syntax::OPERATOR_NOT_NULL, Syntax::OPERATOR_IS_NULL,
            Syntax::OPERATOR_BETWEEN
        ];
    }

    public function display(&$value)
    {
        parent::display($value);
        $value =  DateTool::getDateTime($value, $this->formatter);
    }

    /**
     * 获取显示格式
     *
     * @param IElement $element
     * @return string
     */
    protected function getFormatter($element)
    {
        if ($element->getAttribute()->has('formatter'))
            return $element->getAttribute()->get('formatter');

        switch ($element->getColumnType()) {
            case Syntax::FIELD_DATE : return 'Y-m-d';
            case Syntax::FIELD_TIME : return 'H:i:s';
            default:
                return 'Y-m-d H:i:s';
        }
    }

    public function getFieldType()
    {
        return $this->holder;
    }

    /**
     * 获取FieldType
     *
     * @param IElement $element
     * @return string
     */
    protected function findFieldType($element)
    {
        switch ($element->getColumnType()) {
            case Syntax::FIELD_DATE : return 'date';
            case Syntax::FIELD_TIME : return 'time';
            default:
                return 'datetime';
        }
    }
}

class IdHelper extends IntHelper
{
    /** @noinspection PhpMissingParentCallCommonInspection
     * @param $value
     * @param string $op
     * @return bool
     */
    public function adaptValue(&$value, $op = null)
    {
        return convert_id($value);
    }

    /** @noinspection PhpMissingParentCallCommonInspection */
    public function getCalculators()
    {
        return [];
    }

    /** @noinspection PhpMissingParentCallCommonInspection */
    public function getCompares()
    {
        return ['=', '<>',
            Syntax::OPERATOR_IN, Syntax::OPERATOR_NOT_IN,
            Syntax::OPERATOR_NOT_NULL, Syntax::OPERATOR_IS_NULL,
            Syntax::OPERATOR_BETWEEN
        ];
    }
}

class IpHelper extends IntHelper
{
    public function display(&$value)
    {
        return long2ip($value);
    }
}

class SelectHelper extends IntHelper
{
    protected $options;

    /**
     * DateHelper constructor.
     * @param IElement $element
     */
    public function __construct($element)
    {
        try{
            $this->options = $element->getOptions();
        } catch (\Throwable $e) {
            $this->options = [];
        }

    }

    public function display(&$value)
    {
        $this->adaptValue($value);
        $value = array_isset($this->options, $value, '');
        $value = $value['label'];
    }
}